home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Libris Britannia 4
/
science library(b).zip
/
science library(b)
/
MATHEMAT
/
1025.ZIP
/
LAPLACE.C
< prev
next >
Wrap
C/C++ Source or Header
|
1987-08-15
|
37KB
|
1,207 lines
/*
#define DEBUG1
#define DEBUG2
#define DEBUG4
laplace - solve Laplace's equation for specified boundary conditions
in two dimensions
bugs...
in mark, a line ending near a grid LINE, not just near a grid point,
should be registered.
history...
2 Aug 87 line segments ending near a grid point result in type
zero grid points. type matrix browser installed
(Z-100 only). Several up/down inconsistencies
removed. Crossing display function now shows
coordinates of crosspoint. A type zero grid point
is never redefined, even if other crossings are
recorded for that point. Equations for corner
points have nonzero coefficients only for neighbors
on the grid.
1 Aug 87 -q switch defeats comments during calculation.
28 Jun 86 -g switch sets edge of grid to zero potential,
-m switch specifies a margin around the specified boundaries.
21 Jun 86 Allowing boundary to be between grid points.
31 May 86 showing boundries and calculated potentials are optional.
approximate performance...
h=w=32, n=64
Current time is 20:58:25.76
Current time is 21:00:03.36 -> 5:30.86 or 330.86 sec
h=w=64, n=128
Current time is 21:01:11.29
Current time is 21:07:08.87 -> 5:57.58 or 357.58 sec
...producing 42K file
...on Z-100 with V20 at 7.5 MHz, all files on ramdisk.
h=w=32, n=64
56 seconds
...on IBM AT with 80286 at 8 MHz and 80287 at 5.3 MHz,
all files on ramdisk.
notes...
need to update above performance.
should investigate recursive subdivision by factors other than 2.
can't handle boundary crossing edge of grid: referencing nonexistant
grid points.
needs option for cylindrical symmetry
Should optionally output the grid used, in GRAPH input format.
*/
#define SWITCH
#define DESMET /* deleting this only disables the timing function */
#include <stdio.h>
#include <math.h>
#define VERSION "1.0"
#define MAX_CROSS 375 /* max # times boundary can cross grid */
#define ENTRIES 375 /* maximum # points in boundary point curves */
#define MAXBOUNDARIES 50 /* maximum # separate curves in boundary */
#define PERMITTIVITY 8.85418e-12 /* permittivity of free space, farad/m */
#define SCALE 16384
#define MAX(a, b) ((a)>(b)?(a):(b))
#define streq(s, t) (strcmp(s, t)==0)
typedef struct
{int grid; /* cell number */
int direct; /* direction: 0, 1, 2, 3 for +x, -x, +y, -y */
double dist; /* distance from cell center to boundary crossing point
(fraction of grid spacing, ranging from 0 to 1) */
int volt; /* negative of boundary # (so it's an index into volt[]) */
int dummy[4]; /* ...so sizeof(crossing) >= sizeof(boundary_rule) */
/* >>> delete? <<< */
} crossing;
typedef struct
{int var[4]; /* grid numbers (indexes into volt[]) on which this point
depends. at least one of these will be
negative, corresponding to a boundary value. */
int coef[4]; /* Corresponding coefficients, scaled up by SCALE.
The sum of these four values is approximately SCALE. */
} boundary_rule;
union /* these can share space */
{boundary_rule b_rule[MAX_CROSS+3];
crossing cross_list[MAX_CROSS+3];
} bo;
show_b(b) boundary_rule *b;
{ int i, sum;
sum=0;
for (i=0; i<4; i++) sum += b->coef[i];
printf("rule %d: <>%2d>*%d + <<%2d>*%d + <^%2d>*%d + <v%2d>*%d (ttl wt=%d%s)\n",
b-bo.b_rule,
b->var[0], b->coef[0],
b->var[1], b->coef[1],
b->var[2], b->coef[2],
b->var[3], b->coef[3],
sum,
abs(sum-SCALE)>2?", WRONG!":"");
i=0;
}
int lc; /* index of 1st unused entry in bo.cross_list */
int lb; /* index of 1st unused entry in bo.b_rule */
/*
cells are numbered as follows...
"top"
0 1 2 3 ... w-1 <--- ymin
"left" w w+1 w+2 "right"
2w
...hw-1 <--- ymax
"bottom"
^ ^
| |
xmin xmax
cell types are as follows...
2 3 3 3 4
9 1 1 1 5
9 1 1 1 5
9 1 1 1 5
8 7 7 7 6
and type 0 cells have a specified potential (part of the boundary
conditions) and are never updated. There are special cell types
are for boundaries where the normal gradient vanishes:
10 10 10 10
13 11
13 11
13 11
12 12 12 12
*/
int alpha=0, alphap1=1;
int *type;
int *volt; /* volt[-1]...volt[-boundaries] are boundary voltages
volt[0]...volt[hw-1] are voltages on the grid */
int debugging=0;
int pass=0;
int quiet=0;
int show_resid=0; /* nonzero if residuals are to be shown */
int grounded=0; /* nonzero if edge of grid is to be grounded */
int show_boundaries=0; /* nonzero if boundaries are to be shown */
int show_potentials=0; /* nonzero if calculated potentials are to be shown */
int left_symmetric=0; /* nonzero if y gradient vanishes on left edge of grid */
int right_symmetric=0; /* nonzero if y gradient vanishes on right edge of grid */
int lower_symmetric=0; /* nonzero if x gradient vanishes on lower edge of grid */
int upper_symmetric=0; /* nonzero if x gradient vanishes on upper edge of grid */
int done=0;
long work=0; /* number of cell updates so far */
double xmin=1.e30;
double xmax=-1.e30;
double ymin=1.e30;
double ymax=-1.e30;
double zmin=1.e30;
double zmax=-1.e30;
double zavg;
double margin=0.; /* minimum amount to expand grid beyond given boundaries */
double *x; /* pointer to data area for boundaries */
double *y; /* pointer to data area for boundaries */
double xscale, yscale, zscale;
double edge;
double aspect, correct;
double p_voltage[MAXBOUNDARIES];
double residual();
int boundaries=0; /* number of boundaries in input data */
int x_arguments=0;
int y_arguments=0;
int height=25; /* default height of grid in cells */
int width=25; /* default width of grid in cells */
int cycles=75; /* default # relaxation cycles */
int n; /* number of entries in x and y */
int index_array[MAXBOUNDARIES]; /* indexes into x and y */
int *p_data=index_array;
char outname[40];
FILE file;
FILE ofile;
main (argc, argv) int argc; char **argv;
{ int i, j, k, hw, col;
double yval;
char *s;
read_data(argc, argv);
/*
printf("there are %d points...\n", n);
k=0;
for (i=0; i<n; i++)
{printf("%f %f\n", x[i], y[i]);
if(i==p_data[k]) printf(" ...at voltage %f\n", p_voltage[k++]);
}
*/
if(s=index(argv[1], '.')) strncpy(outname, argv[1], s-argv[1]);
else strcpy(outname, argv[1]);
strcat(outname, ".pot");
ofile=fopen(outname, "w");
if(ofile==0) {printf("can\'t create output file\n"); exit(1);}
/* increase grid height or width to approximate aspect ratio of data */
aspect=(ymax-ymin)/(xmax-xmin);
if(height-1<(width-1)*aspect)
height=1|(int)((width-1)*aspect+1); /* odd number allows recursion */
else width=1|(int)((height-1)/aspect+1);
/* for periodic x boundaries, grid width must match data exactly */
if(left_symmetric && right_symmetric)
while((height-1)<(width-1)*aspect)
height += 2;
/* increase data width or height to match grid */
if(left_symmetric && right_symmetric && upper_symmetric && lower_symmetric
&& fabs((height-1)-(width-1)*aspect)>.002*(height-1))
{printf("doubly periodic boundary (-xp and -yp) conditions require\n");
printf("(height-1)/(width-1) = (ymax-ymin)/(xmax-xmin)\n");
exit(1);
}
if((height-1)<(width-1)*aspect)
{correct=(ymax-ymin)*(width-1)/(height-1.)-(xmax-xmin);
if(left_symmetric) xmax += correct;
else {xmin -= correct/2.; xmax += correct/2.;}
}
else
{correct=(xmax-xmin)*(height-1)/(width-1.)-(ymax-ymin);
if(lower_symmetric) ymax += correct;
else {ymin -= correct/2.; ymax += correct/2.;}
}
hw=height*width;
if(cycles<height+width) cycles=height+width;
type=malloc(hw*sizeof(int));
volt=malloc((boundaries+hw)*sizeof(int));
if(!type || !volt)
{fprintf(stderr, "not enough workspace for %d by %d grid", height, width);
exit(1);
}
volt += boundaries; /* allow space for the boundary potentials */
laplace(type, volt, width, height, cycles);
if(show_potentials) show_volt("", type, volt, width, height);
#ifdef FIXED
charges(type, volt, width, height);
#endif
if(!quiet) printf("writing result to file %s\n", outname);
if(-1==fprintf(ofile, "%10.4g%10.4g%5d minimum and maximum x values, width\n", xmin, xmax, width)) exit(1);
if(-1==fprintf(ofile, "%10.4g%10.4g%5d minimum and maximum y values, height\n", ymin, ymax, height)) exit(1);
yval=ymin;
for (i=0; i<height; i++)
{col=0;
for (j=0; j<width; j++)
{if(-1==fprintf(ofile, "%10.4g", volt[i*width+j]/zscale+zavg))
exit(1);
if(++col>=7)
{if(-1==fprintf(ofile, "\n")) exit(1);
col=0;
}
}
if(col)
if(-1==fprintf(ofile, "\n")) exit(1);
}
fclose(ofile);
}
laplace (type, volt, width, height, cycles) int *type; int *volt, width, height, cycles;
{
#ifdef DESMET
long start, tics();
#endif
int i, j, k, hw;
hw=height*width;
if(height<width) i=height; else i=width;
if(i>4 && (width&1)==1 && (height&1)==1)
{laplace(type, volt, width/2+1, height/2+1, cycles/2);
/* show_volt("potentials on coarse grid", type, volt, width/2+1, height/2+1); */
i=hw; j=(height/2+1)*(width/2+1);
while(i>width) /* copy voltages into refined grid */
{i--; j--;
volt[i]=volt[i-width]=volt[j];
if(i%width==0) i -= width;
else {volt[i-1]=volt[i-width-1]=volt[j]; i--;}
}
while(i>1)
{i--; j--;
volt[i]=volt[i-1]=volt[j];
i--;
}
/* show_volt("potentials on fine grid, fully transferred", type, volt, width, height); */
}
else
{for (i=0; i<hw; i++) volt[i]=0;
}
if(!quiet) printf("pass %d: height=%d, width=%d, cycles=%d\n",
++pass, height, width, cycles);
#ifdef DESMET
start=tics();
#endif
fill(type, volt, width, height);
k=0;
if(show_boundaries)
/* {for (i=height-1; i>=0; i--) */
{for (i=0; i<height; i++)
{k=i*width;
printf("%3d: ", k);
for (j=0; j<width; j++)
printf("%5d", type[k++]);
printf("\n");
}
if(debugging)
{int c, g=0, t;
while(1)
{t=type[g];
printf("<%2d>: type %d \n", g, t);
if(t<0) show_b(&bo.b_rule[-t]);
else if(t==0) printf("potential=%f\033K\n", volt[g]/zscale+zavg);
else printf("\033K\n");
printf("> \008"); c=getchar();
if(c==56) {if(g>=width) g-=width;} /* up */
else if(c==50) {if(g<height*width-width) g+=width;} /* down */
else if(c==54) {if(g<height*width-1) g+=1;} /* right */
else if(c==52) {if(g>0) g-=1;} /* left */
else if(c=='q') break;
printf("\015\033A\033A");
}
}
}
/* show_volt("", type, volt, width, height); */
while(cycles--)
{if(!show_resid && !quiet)
printf("\015 %3d cycles to go ", cycles);
relax(type, volt, width, height);
work += hw;
if(show_resid && ++done%5==0)
printf("%D %f\n", work, residual(type, volt, width, height));
}
#ifdef DESMET
if(!quiet) printf("\015 finished in %4.2f sec \n", .01*(tics()-start));
#else
if(!quiet) printf("\015 finished \n");
#endif
}
relax (type, v, w, h) int *type; int *v, w, h;
{ int i, wh;
int hm1, wm1, j;
boundary_rule *rule;
long sum;
#ifdef DEBUG3
char *typ=type;
#endif
#ifdef SWITCH
i=wh=w*h;
while(i--) /* loop over all grid points */
{if(*type>0) /* grid point not next to boundaries */
{switch(*type)
{
case 1: v[0]= ( v[-w] + v[-1] + v[1] + v[w] + 2)/4; break;
case 2: v[0]= ( v[1] + v[w] + 1)/2; break;
case 3: v[0]= ( 2*v[-1] + 2*v[1] + v[w] + 2)/5; break;
case 4: v[0]= ( v[-1] + v[w] + 1)/2; break;
case 5: v[0]= (2*v[-w] + v[-1] + 2*v[w] + 2)/5; break;
case 6: v[0]= ( v[-w] + v[-1] + 1)/2; break;
case 7: v[0]= ( v[-w] + 2*v[-1] + 2*v[1] + 2)/5; break;
case 8: v[0]= ( v[-w] + v[1] + 1)/2; break;
case 9: v[0]= (2*v[-w] + v[1] + 2*v[w] + 2)/5; break;
case 10: v[0]= (2*v[ w] + v[-1] + v[1] + 2)/4; break;
case 11: v[0]= ( v[-w] + 2*v[-1] + v[ w] + 2)/4; break;
case 12: v[0]= ( v[-1] + v[1] + 2*v[-w] + 2)/4; break;
case 13: v[0]= ( v[-w] + 2*v[1] + v[ w] + 2)/4; break;
}
}
else if (*type<0) /* grid point next to boundaries */
{rule=&bo.b_rule[-*type];
#ifdef DEBUG3
printf("\ngrid %d: ", wh-i-1); show_b(rule);
show_volt("", typ, volt, w, h);
#endif
sum = volt[rule.var[0]]*(long)rule.coef[0]
+volt[rule.var[1]]*(long)rule.coef[1]
+volt[rule.var[2]]*(long)rule.coef[2]
+volt[rule.var[3]]*(long)rule.coef[3];
v[0]=sum/SCALE;
#ifdef DEBUG3
printf("setting potential to %5.2f\n", v[0]/zscale+zavg);
#endif
}
#ifdef DEBUG3
if(v[0]/zscale+zavg<zmin || v[0]/zscale+zavg>zmax || (v-volt)%11==5)
{printf("\ngrid %d, of type %d\n", v-volt, type[0]);
show_star(v, w);
if(type[0]<0)
{int i;
show_b(&bo.b_rule[-*type]);
printf("\nboundary potentials:");
for (i=1; i<=boundaries; i++)
printf(" <%3d>=%6d=>%6.3f",
-i, volt[-i], volt[-i]/zscale+zavg);
printf("\n");
printf("<%3d> %6d * %5ld +\n", rule.var[0], volt[rule.var[0]], (long)rule.coef[0]);
printf("<%3d> %6d * %5ld +\n", rule.var[1], volt[rule.var[1]], (long)rule.coef[1]);
printf("<%3d> %6d * %5ld +\n", rule.var[2], volt[rule.var[2]], (long)rule.coef[2]);
printf("<%3d> %6d * %5ld = sum = %ld => %ld => %6.3f\n",
rule.var[3], volt[rule.var[3]], (long)rule.coef[3],
sum, sum/SCALE, sum/SCALE/zscale+zavg);
}
}
#endif
v++; type++;
}
#else /* not SWITCH */
hm1=h-1;
wm1=w-1;
if(*type++)
v[0]= ( v[1] + v[w] + 1)/2;
v++;
for (j=1; j<wm1; j++)
{if(*type++)
v[0]= ( v[-1] + v[1] + v[w] + 1)/3;
v++;
}
if(*type++)
v[0]= ( v[-1] + v[w] + 1)/2;
v++;
for (i=1; i<hm1; i++)
{
if(*type++)
v[0]= (v[-w] + v[1] + v[w] + 1)/3;
v++;
for (j=1; j<wm1; j++)
{if(*type++)
v[0]= (v[-w] + v[-1] + v[1] + v[w] + 2)/4;
v++;
}
if(*type++)
v[0]= (v[-w] + v[-1] + v[w] + 1)/3;
v++;
}
if(*type++)
v[0]= (v[-w] + v[1] + 1)/2;
v++;
for (j=1; j<wm1; j++)
{if(*type++)
v[0]= (v[-w] + v[-1] + v[1] + 1)/3;
v++;
}
if(*type++)
v[0]= (v[-w] + v[-1] + 1)/2;
v++;
#endif
}
show_star(x, w) int *x, w;
{ printf(" "); show_point(x-w); printf("\n");
show_point(x-1); show_point(x); show_point(x+1); printf("\n");
printf(" "); show_point(x+w); printf("\n");
}
show_point(x) int *x;
{ printf(" <%3d>=%6d=>%6.3f", x-volt, *x, *x/zscale+zavg);
}
double residual (type, volt, w, h) int *type; int *volt, w, h;
{ int i, hw, n;
int hm1, wm1, j;
long r, d;
n=0;
i=hw=w*h;
r=0L;
while(i--)
{switch(*type++)
{case 0: n++; break;
case 1: d=volt[0]-(volt[-w] + volt[-1] + volt[1] + volt[w] + 2)/4; r+=d*d; break;
case 2: d=volt[0]-( volt[1] + volt[w] + 1)/2; r+=d*d; break;
case 3: d=volt[0]-( volt[-1] + volt[1] + volt[w] + 1)/3; r+=d*d; break;
case 4: d=volt[0]-( volt[-1] + volt[w] + 1)/2; r+=d*d; break;
case 5: d=volt[0]-(volt[-w] + volt[-1] + volt[w] + 1)/3; r+=d*d; break;
case 6: d=volt[0]-(volt[-w] + volt[-1] + 1)/2; r+=d*d; break;
case 7: d=volt[0]-(volt[-w] + volt[-1] + volt[1] + 1)/3; r+=d*d; break;
case 8: d=volt[0]-(volt[-w] + volt[1] + 1)/2; r+=d*d; break;
case 9: d=volt[0]-(volt[-w] + volt[1] + volt[w] + 1)/3; r+=d*d; break;
}
volt++;
}
if (hw<=n)
return 1.;
return sqrt(((double)r)/(hw-n));
}
#ifdef FIXED
/*
this no longer works...could be changed to solve linear system for
the gradient at center of each grid point next to a boundary and
integrate, but it's easier to let CONTOUR calculate the integral.
*/
charges (type, volt, width, height) int *type; int *volt, width, height;
{ int v, i, plate=0, out=0, hw;
long charge;
double C, Z, q[2], plate_voltage[2], sum=0.;
hw=height*width;
printf("\nComputing surface integral of grad V over each boundary\n");
printf("(integral of -grad V dot N, where N is normal to the boundary)...\n");
printf("\nboundary potential surface integral charge\n");
while(1)
{for (i=0; i<hw; i++)
if(type[i]==0)
break;
if(i==hw) break;
v=volt[i];
plate_voltage[plate&1]=v/zscale+zavg;
charge=0.;
if(i%width) charge += v-volt[i-1];
if(i>=width) charge += v-volt[i-width];
if(i<hw-width) charge += v-volt[i+width];
if(i%width != width-1) charge += v-volt[i+1];
type[i]=23;
for (i++; i<hw; i++)
{if(type[i]==0 && v==volt[i])
{if(i%width) charge += v-volt[i-1];
if(i>=width) charge += v-volt[i-width];
if(i<hw-width) charge += v-volt[i+width];
if(i%width != width-1) charge += v-volt[i+1];
type[i]=23; /* don't look at this cell again */
}
}
sum += (q[plate&1]=(charge/zscale)*PERMITTIVITY);
printf("%5d %14.2g %15.3g %15.3g\n",
plate, v/zscale+zavg, q[plate&1]/PERMITTIVITY, q[plate&1]);
plate++;
}
printf(" sum (should be zero)%12.3g %15.3g\n\n", sum/PERMITTIVITY, sum);
if(plate==2)
{q[0]=fabs(q[0]);
q[1]=fabs(q[1]);
C=MAX(q[1], q[0])/fabs(plate_voltage[1]-plate_voltage[0]);
Z=1./C/2.9979e8;
printf("characteristic impedance is %5.2f ohms\n", Z);
printf("capacitance is %7.3g farad/m\n", C);
printf("inductance is %7.3g henries/m\n", Z/2.9979e8);
}
}
#endif
/*
the equations relating a grid point and its four neighbors, in the
absence of a boundary, are:
( 0 1 1 1) (Uy ) (Uright)
( 0 -1 1 1) (Ux ) = (Uleft )
( 1 0 -1 1) (Uxx ) (Uup )
(-1 0 -1 1) (Uhere) (Udown )
where Ux is h*(d/dx U)
Uy is h*(d/dy U)
Uxx is .5*h**2*(d^2/dx^2 U))
near a boundary, the equations are:
( 0 R R*R 1) (Uy ) (Uright)
( 0 -L L*L 1) (Ux ) = (Uleft )
( U 0 -U*U 1) (Uxx ) (Uup )
(-D 0 -D*D 1) (Uhere) (Udown )
where neighbors to right, left, up, and down are at distances R, L,
U, and D respectively.
FILL inverts the matrix and saves the last row of the inverse. The
dot product of that row with the right hand side of the above
equation yields Uhere, the revised value of the potential at the
center.
*/
double lhs[16]=
{0., 1., 1., 1.,
0., -1., 1., 1.,
1., 0., -1., 1.,
-1., 0., -1., 1.};
#define LEFT_SIDE(g) ((g)%w==0)
#define RIGHT_SIDE(g) ((g)%w==w-1)
#define TOP_SIDE(g) ((g)<w)
#define LOWER_SIDE(g) ((g)>=hw-w)
/* fill type and voltage arrays based on user's boundary conditions */
fill (type, volt, w, h) int *type; int *volt, w, h;
{ boundary_rule *ru;
crossing *this, *last;
double invert(), decomp();
double a[4][4], ai[4][4], cond, condp1, *p, f;
double xt, yt, xt2, yt2;
int i, j, g, zero, *ip;
long weight;
int compare();
int i, k, hw, zz;
if(!quiet) printf(" following boundaries \n");
hw=h*w;
xscale=(w-1)/(xmax-xmin);
yscale=(h-1)/(ymax-ymin); /* same as xscale */
if(fabs(xscale-yscale)>.001*fabs(yscale))
{printf("grid isn't square... xmin=%f, xmax=%f, w=%d, xscale=%f\n",
xmin, xmax, w, xscale);
printf(" ymin=%f, ymax=%f, w=%d, yscale=%f\n",
ymin, ymax, h, yscale);
}
edge=1./xscale;
#ifdef DEBUG1
printf("xscale=%f, yscale=%f, edge=%f\n", xscale, yscale, edge);
#else
#ifdef DEBUG2
printf("xscale=%f, yscale=%f, edge=%f\n", xscale, yscale, edge);
#endif /* DEBUG2 */
#endif /* DEBUG1 */
for (i=0; i<hw; i++) type[i]=1; /* interior */
if(grounded)
{zero=floor((0.-zavg)*zscale+.499);
for (i=w; i<hw; i+=w) {type[i]=0; volt[i]=zero;} /* left */
for (i=0; i<w; i++) {type[i]=0; volt[i]=zero;} /* top */
for (i=w-1; i<hw; i+=w) {type[i]=0; volt[i]=zero;} /* right */
for (i=hw-w; i<hw; i++) {type[i]=0; volt[i]=zero;} /* bottom */
}
else
{if(left_symmetric)
for (i=w; i<hw; i+=w) type[i]=13; /* left */
else
for (i=w; i<hw; i+=w) type[i]=9; /* left */
for (i=0; i<w; i++) type[i]=3; /* top */
for (i=w-1; i<hw; i+=w) type[i]=5; /* right */
if(lower_symmetric)
for (i=hw-w; i<hw; i++) type[i]=12; /* bottom */
else
for (i=hw-w; i<hw; i++) type[i]=7; /* bottom */
type[0]=2; type[w-1]=4; type[hw-w]=8; type[hw-1]=6; /* corners */
}
i=k=0;
lc=2; /* bo.cross_list is empty */
while(i<n)
{xt=x[i]-xmin;
yt=y[i]-ymin;
zz=floor((p_voltage[k]-zavg)*zscale+.499);
volt[-k-1]=zz;
do {xt2=x[i]-xmin;
yt2=y[i]-ymin;
mark(w, h, xt, yt, xt2, yt2, -k-1);
/*
printf("%f, %f -> ", (x[i]-xmin)*xscale, (y[i]-ymin)*yscale);
printf("mark(%d, %d, %d, %d, %d, %d, %d)\n", w, h, xx, yy, xx2, yy2, zz);
*/
xt=xt2; yt=yt2;
i++;
} while(i<=p_data[k]);
k++;
}
if(!quiet) printf(" boundary crosses grid %d times\n", lc-1);
/*---------------- sort list -------------*/
hsort(&bo.cross_list[2], lc-2, sizeof(crossing), &compare);
/*---------------- solve each problem -------------*/
#ifdef DEBUG2
printf("crossings after sorting\n");
for (i=2; i<17; i++) show_c(&bo.cross_list[i], w); getchar();
#endif /* DEBUG2 */
#ifdef DEBUG4
{int g; double xx, yy, d; FILE *out;
out=fopen("points","w");
for (i=2; i<=lc; i++)
{g=bo.cross_list[i].grid;
xx=(g%w)*edge+xmin; yy=(g/w)*edge+ymin;
d=bo.cross_list[i].dist*edge;
switch(bo.cross_list[i].direct)
{case 0: xx+=d; break; /* right */
case 1: xx-=d; break; /* left */
case 2: yy-=d; break; /* above */
case 3: yy+=d; break; /* below */
}
fprintf(out, "%10.3f %10.3f\n", xx, yy);
}
fclose(out);
}
#endif /* DEBUG4 */
lb=1; /* bo.b_rule is empty */
this=&bo.cross_list[2];
last=&bo.cross_list[lc];
last->grid=hw; /* sentinal */
while(this<last)
{p=a;
for (i=0; i<16; i++) p[i]=lhs[i];
g=this->grid;
if(g<0) {this++; continue;}
if(type[g]==0) {while((++this)->grid==g){} continue;}
if(g>=hw) break;
#ifdef DEBUG2
printf("rule %d, boundary conditions for grid point %d\n", lb, g);
#endif
/* set voltage for this grid to that of the
first boundary in the list (the closest one) */
volt[g]=floor((p_voltage[-this->volt-1]-zavg)*zscale+.499);
ru=&bo.b_rule[lb];
/* initialize variables to refer to neighbors */
ru->var[0]=g+1; /* right */
ru->var[1]=g-1; /* left */
ru->var[2]=g-w; /* up */
ru->var[3]=g+w; /* down */
do {j=this->direct;
#ifdef DEBUG2
printf(" applying:"); show_c(this, w);
#endif
f=this->dist;
if(f<edge*.01) /* if boundary is very close, */
{type[g]=0; /* declare the point to be type 0 */
while((++this)->grid==g){}
goto NEXT_GRID;
}
ru->var[j]=this->volt; /* point to boundary instead of neighbor */
a[j][0] *= f;
a[j][1] *= f;
a[j][2] *= f*f;
while((++this)->grid==g && this->direct==j)
{
#ifdef DEBUG2
printf(" skipping:"); show_c(this, w);
#endif
}
} while (this->grid==g);
/* show_m("boundary condition matrix", a); getchar(); */
cond=invert(4, 4, a, ai);
/* printf("condition number is %f\n", cond);
show_m("matrix inverse", ai); */
condp1=cond+1.;
if(cond==condp1)
{fprintf(stderr,
"can\'t solve linear system for boundary conditions\n");
while((--this)->grid==g) {fprintf(stderr, " "); show_c(this, w);}
exit(1);
}
ru->coef[0]=ai[3][0]*SCALE+.5; /* unrolled for speed */
ru->coef[1]=ai[3][1]*SCALE+.5;
ru->coef[2]=ai[3][2]*SCALE+.5;
ru->coef[3]=ai[3][3]*SCALE+.5;
if(LOWER_SIDE(g)||LEFT_SIDE(g)||TOP_SIDE(g)||RIGHT_SIDE(g))
{
#ifdef DEBUG2
printf("rule for grid point %d, ", g);
if(LOWER_SIDE(g)) printf("LOWER ");
if(LEFT_SIDE(g)) printf("LEFT ");
if(TOP_SIDE(g)) printf("TOP ");
if(RIGHT_SIDE(g)) printf("RIGHT ");
printf("\n");
printf("before adjustment ");show_b(ru);
#endif
}
if(LOWER_SIDE(g))
{if(lower_symmetric)
ru->coef[2] += ru->coef[3];
else
{weight=ru->coef[2]=(ru->coef[2] + ru->coef[3])/4;
weight = (SCALE*(long)SCALE)/(weight+ru->coef[0]+ru->coef[1]);
ru->coef[0] = (ru->coef[0]*weight)/SCALE;
ru->coef[1] = (ru->coef[1]*weight)/SCALE;
ru->coef[2] = (ru->coef[2]*weight)/SCALE;
}
ru->coef[3]=0;
}
else if(TOP_SIDE(g))
{if(upper_symmetric)
ru->coef[3] += ru->coef[2];
else
{weight=ru->coef[3]=(ru->coef[2] + ru->coef[3])/4;
weight = (SCALE*(long)SCALE)/(weight+ru->coef[0]+ru->coef[1]);
ru->coef[0] = (ru->coef[0]*weight)/SCALE;
ru->coef[1] = (ru->coef[1]*weight)/SCALE;
ru->coef[3] = (ru->coef[3]*weight)/SCALE;
}
ru->coef[2]=0;
}
if(LEFT_SIDE(g))
{if(left_symmetric)
ru->coef[0] += ru->coef[1];
else
{weight=ru->coef[0]=(ru->coef[0] + ru->coef[1])/4;
weight = (SCALE*(long)SCALE)/(weight+ru->coef[2]+ru->coef[3]);
ru->coef[1] = (ru->coef[1]*weight)/SCALE;
ru->coef[2] = (ru->coef[2]*weight)/SCALE;
ru->coef[3] = (ru->coef[3]*weight)/SCALE;
}
ru->coef[1]=0;
}
else if(RIGHT_SIDE(g))
{if(right_symmetric)
ru->coef[1] += ru->coef[0];
else
{weight=ru->coef[1]=(ru->coef[1] + ru->coef[0])/4;
weight = (SCALE*(long)SCALE)/(weight+ru->coef[2]+ru->coef[3]);
ru->coef[1] = (ru->coef[1]*weight)/SCALE;
ru->coef[2] = (ru->coef[2]*weight)/SCALE;
ru->coef[3] = (ru->coef[3]*weight)/SCALE;
}
ru->coef[0]=0;
}
#ifdef DEBUG2
printf(" ");show_b(ru);
#endif
type[g]=-lb;
lb++;
NEXT_GRID:
;
}
if(!quiet) printf(" %d grid points have special equations\n", lb-1);
}
compare(a, b) crossing *a, *b;
{ int n;
if(n=(a->grid - b->grid)) return n;
if(n=(a->direct - b->direct)) return n;
if(a->dist < b->dist) return -1;
return 1; /* if same distance away, declare a > b */
}
mark (width, height, x1, y1, x2, y2, v)
int width, height, /* dimensions of grid */
v; /* potential of boundary segment is volt[-v-1] */
double x1, y1, x2, y2; /* beginning and end points of boundary segment */
{ int q, dx, dy, ax, ay, decide, up, over, i;
int nx, nx2, ny, ny2;
double del, xe, ye, f, t;
/*
if(x1<0||x1>=width||x2<0||x2>=width||y1<0||y1>=height||y2<0||y2>=height)
{fprintf(stderr, "calling sequence error: mark(%d, %d, %d, %d, %d, %d)\n",
width, x1, y1, x2, y2, v);
fprintf(stderr, "(point outside 0<=x<%d or 0<=y<%d)\n", width, height);
exit(1);
}
*/
#ifdef DEBUG1
printf("line from %5.2f, %5.2f to %5.2f, %5.2f at potential %5.2f\n",
x1, y1, x2, y2, p_voltage[-v-1]);
printf(" slope = dy/dx = %5.2f/%5.2f\n", y2-y1, x2-x1);
#endif
/* test whether endpoint is near a gridpoint */
nx2=x2*xscale+.5;
ny2=y2*yscale+.5;
if(fabs(nx2*edge-x2)+fabs(ny2*edge-y2)<.01*edge)
register_crossing(width, nx2, ny2, 0., 2, v);
if(x2<x1) {t=x1; x1=x2; x2=t; t=y1; y1=y2; y2=t;}
/* now, x1 <= x2 */
nx2=x2*xscale;
nx=x1*xscale+1;
if(nx<=nx2)
{xe=nx*edge;
del=yscale/(x2-x1);
ye=(y1*(x2-xe) + y2*(xe-x1))*del;
del *= (y2-y1)*edge;
for ( ; nx<=nx2; nx++)
{ny=ye;
f=ye-ny;
#ifdef DEBUG1
printf(" crosses y grid line at %5.3f, %5.3f: %5.3f below <%d> = (%d,%d)\n",
xe, ye, f, nx+ny*width, nx, ny);
#endif
register_crossing(width, nx, ny, f, 3, v); /* below this point */
register_crossing(width, nx, ny+1, 1.-f, 2, v); /* above this point */
xe += edge;
ye += del;
}
}
if(y2<y1) {t=x1; x1=x2; x2=t; t=y1; y1=y2; y2=t;}
/* now, y1 <= y2 */
ny2=y2*yscale;
ny=y1*yscale+1;
if(ny<=ny2)
{ye=ny*edge;
del=xscale/(y2-y1);
xe=(x1*(y2-ye) + x2*(ye-y1))*del;
del *= (x2-x1)*edge;
for ( ; ny<=ny2; ny++)
{nx=xe;
f=xe-nx;
#ifdef DEBUG1
printf(" crosses x grid line at %5.3f, %5.3f: %5.3f right of <%d> = (%d,%d)\n",
xe, ye, f, nx+ny*width, nx, ny);
#endif
register_crossing(width, nx, ny, f, 0, v); /* right of this */
register_crossing(width, nx+1, ny, 1.-f, 1, v); /* left of this */
ye += edge;
xe += del;
}
}
}
register_crossing(width, nx, ny, f, direction, v)
int width, nx, ny, direction, v;
double f;
{ int g;
crossing *new;
g=nx+ny*width;
if(type[g]==0) return;
if(f<.01)
{type[g]=0;
volt[g]=floor((p_voltage[-v-1]-zavg)*zscale+.499);
#ifdef DEBUG1
printf(" <%d> set to %3.2f, type 0\n", g, p_voltage[-v-1]);
#endif
return;
}
if(f>.99) return;
if(lc>=MAX_CROSS)
{fprintf(stderr, "too many boundary crosspoints\n");
exit(1);
}
new=&bo.cross_list[lc++];
new->grid=g;
new->direct=direction;
new->dist=f;
new->volt=v;
#ifdef DEBUG1
printf(" "); show_c(new, width);
#endif
}
show_volt (msg, type, volt, w, h) char *msg; int *type; int *volt, w, h;
{ int i, j, k;
k=0;
printf("%s\nboundaries...", msg);
for (i=1; i<=boundaries; i++) printf("%d: %5.2f ", i, volt[-i]/zscale+zavg);
printf("\n");
/* printf("(press any key to abort printout)\n");
for (i=0; i<h && !csts(); i++)
*/
for (i=0; i<h; i++)
{k=i*w;
printf("%3d: ", k);
for (j=0; j<w; j++)
{printf("%6.2f", volt[k++]/zscale+zavg);
/* printf("%6d", volt[k++]); */
}
printf("\n");
}
for (i=0; i<h; i++)
{k=i*w;
printf("%3d: ", k);
for (j=0; j<w; j++)
printf("%6d", type[k++]);
printf("\n");
}
if(debugging) getchar();
}
read_data(argc, argv) int argc; char **argv;
{ int i, j, length;
double xx, yy, d, *pd, sum;
char *s, *t;
#define BUFSIZE 200
static char buf[BUFSIZE];
x=malloc(ENTRIES*sizeof(double));
y=malloc(ENTRIES*sizeof(double));
if(x==0 || y==0) {fprintf(stderr, "can\'t allocate buffer"); exit();}
if(argc>1 && streq(argv[1], "?")) help();
if(argc<=1 || *argv[1]=='-') file=stdin;
else
{if(argc>1)
{file=fopen(argv[1], "r");
if(file==0) {printf("file %s not found\n", argv[1]); exit();}
argc--; argv++;
}
else help();
}
argc--; argv++;
while(argc>0)
{i=get_parameter(argc, argv);
argv=argv+i; argc=argc-i;
}
p_data[0]=-1;
if(height<2||width<2||cycles<2)
{fprintf(stderr, "parameter too small: height=%d, width=%d, cycles=%d",
height, width, cycles);
exit(1);
}
i=0;
while(i<ENTRIES)
{if(fgets(buf, BUFSIZE, file)==0) break;
t=buf;
while(*t && isspace(*t)) t++;
if(*t == 0) continue; /* skip blank lines */
buf[strlen(buf)-1]=0; /* zero out the line feed */
if(buf[0]==';') {printf("%s\n", buf); continue;} /* skip comment */
sscanf(buf, "%F %F", &x[i], &y[i]);
if (x[i]<xmin) xmin=x[i];
if (x[i]>xmax) xmax=x[i];
if (y[i]<ymin) ymin=y[i];
if (y[i]>ymax) ymax=y[i];
s=buf; /* start looking for label */
while(*s==' ')s++; /* skip first number */
while(*s && (*s!=' '))s++;
while(*s==' ')s++; /* skip second number */
while(*s && (*s!=' '))s++;
while(*s==' ')s++;
if((length=strlen(s))&&(boundaries<MAXBOUNDARIES))
{if(*s=='\"') s++; /* label in quotes */
sscanf(s, "%F", &p_voltage[boundaries]);
if(p_voltage[boundaries]<zmin) zmin=p_voltage[boundaries];
if(p_voltage[boundaries]>zmax) zmax=p_voltage[boundaries];
p_data[boundaries++]=i;
}
i++;
}
n=i;
if(grounded)
{if(0.<zmin) zmin=0.;
if(0.>zmax) zmax=0.;
}
if(!boundaries || p_data[boundaries-1]!=n-1)
{p_data[boundaries]=i-1;
p_voltage[boundaries++]=0.;
}
zscale=65536./5./(zmax-zmin);
zavg=(zmax+zmin)/2.;
xmin -= margin;
xmax += margin;
ymin -= margin;
ymax += margin;
}
/* get_parameter - process one command line option
(return # parameters used) */
get_parameter(argc, argv) int argc; char **argv;
{ int i;
double temp;
if(streq(*argv, "-d")) {debugging=1; return 1;}
else if(streq(*argv, "-q")) {quiet=1; return 1;}
else if(streq(*argv, "-h"))
{if((argc>1) && numeric(argv[1])) height=atoi(argv[1]);
return 2;
}
else if(streq(*argv, "-w"))
{if((argc>1) && numeric(argv[1])) width=atoi(argv[1]);
return 2;
}
else if(streq(*argv, "-g"))
{grounded++;
return 1;
}
else if(streq(*argv, "-m"))
{i=get_double(argc, argv, 1, &margin, &margin, &margin);
if(margin<0.) margin=0.;
return i;
}
else if(streq(*argv, "-n"))
{if((argc>1) && numeric(argv[1])) cycles=atoi(argv[1]);
return 2;
}
else if(streq(*argv, "-r")) {show_resid=1; return 1;}
else if(streq(*argv, "-b")) {show_boundaries=1; return 1;}
else if(streq(*argv, "-p")) {show_potentials=1; return 1;}
/* else if(streq(*argv, "-a"))
{i=get_double(argc, argv, 1, &temp, &temp, &temp);
alpha=temp;
alphap1=alpha+1;
return i;
}
*/
else if(streq(*argv, "-x"))
{i=get_double(argc, argv, 2, &xmin, &xmax, &xmax);
x_arguments=i-1;
return i;
}
else if(streq(*argv, "-y"))
{i=get_double(argc, argv, 2, &ymin, &ymax, &ymax);
y_arguments=i-1;
return i;
}
else if(streq(*argv, "-ys")) {lower_symmetric=1; return 1;}
else if(streq(*argv, "-xs")) {left_symmetric=1; return 1;}
else if(streq(*argv, "-xp")) {right_symmetric=left_symmetric=1; return 1;}
else if(streq(*argv, "-yp")) {upper_symmetric=lower_symmetric=1; return 1;}
else gripe(argv);
}
get_double(argc, argv, permitted, a, b, c)
int argc, permitted; char **argv; double *a, *b, *c;
{ int i=1;
if((permitted--)>0 && (argc>i) && numeric(argv[i])) *a=atof(argv[i++]);
if((permitted--)>0 && (argc>i) && numeric(argv[i])) *b=atof(argv[i++]);
if((permitted--)>0 && (argc>i) && numeric(argv[i])) *c=atof(argv[i++]);
return i;
}
gripe_arg(s) char *s;
{ fprintf(stderr, "argument missing for switch %s", s);
help();
}
gripe(argv) char **argv;
{ fprintf(stderr, *argv); fprintf(stderr, " isn\'t a legal argument \n\n");
help();
}
help()
{ printf("laplace version %s", VERSION);
printf("\nusage: laplace [file][options]\n");
printf("options are:\n");
/* printf(" -a num acceleration factor (default %d) \n", alpha); */
printf(" -b display boundaries\n");
printf(" -g edge of grid is grounded (potential 0)\n");
printf(" -p display calculated potentials\n");
printf(" -q quiet: no comments during calculation\n");
printf(" -r calculate and display residuals\n");
printf(" -m margin distance to expand grid beyond given boundaries (default 0) \n");
printf(" -n num number of relaxation cycles (default %d) \n", cycles);
printf(" -h num height of grid in cells (default %d)\n", height);
printf(" -w num width of grid in cells (default %d)\n", width);
printf(" -x [min [max]] specify x range \n");
printf(" -y [min [max]] specify y range \n");
printf(" -xs symmetric across the line x=xmin \n");
printf(" -ys symmetric across the line y=ymin \n");
printf(" -xp periodic: symmetric across lines x=xmin and x=xmax \n");
printf(" -yp periodic: symmetric across lines y=ymin and y=ymax \n");
exit();
}
numeric(s) char *s;
{ char c;
while(c=*s++)
{if((c<='9' && c>='0') || c=='+' || c=='-' || c=='.') continue;
return 0;
}
return 1;
}
#ifdef DESMET
/*
tics - report hundreths of a second since midnight
*/
long tics()
{ int hr, min, sec, hun;
_timer(&hr, &min, &sec, &hun);
return (( (long) hr*60+min)*60+sec)*100+hun;
}
_timer()
{
#asm
mov ah, 2ch
int 21h
mov bx, [bp+4]
mov [bx], ch ;hours
mov byte [bx+1], 0
mov bx, [bp+6]
mov [bx], cl ;minutes
mov byte [bx+1], 0
mov bx, [bp+8]
mov [bx], dh ;seconds
mov byte [bx+1], 0
mov bx, [bp+10]
mov [bx], dl ;hundreths
mov byte [bx+1], 0
#endasm
}
#endif
show_m(s, a) char *s; double *a;
{ int i, j;
printf("%s\n", s);
for (i=0; i<4; i++)
{for (j=0; j<4; j++) printf("%8.2f ", *a++);
printf("\n");
}
}
char *c_label[4]={"right of", "left of", "above", "below"};
show_c(c, w) crossing *c; int w;
{ double xx, yy, d;
int g;
g=c->grid;
xx=(g%w)*edge+xmin; yy=(g/w)*edge+ymin;
d=c->dist*edge;
switch(c->direct)
{case 0: xx+=d; break; /* right */
case 1: xx-=d; break; /* left */
case 2: yy-=d; break; /* above */
case 3: yy+=d; break; /* below */
}
printf("boundary value %d is %4.3f %s grid %d =(%4.3f,%4.3f)",
c->volt, c->dist, c_label[c->direct], c->grid, xx, yy);
}